package com.shm.singlecamera.Camera;

import android.Manifest;
import android.app.Activity;
import android.content.Context;
import android.content.pm.PackageManager;
import android.graphics.ImageFormat;
import android.graphics.SurfaceTexture;
import android.hardware.camera2.CameraAccessException;
import android.hardware.camera2.CameraCaptureSession;
import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CameraDevice;
import android.hardware.camera2.CameraManager;
import android.hardware.camera2.CaptureRequest;
import android.hardware.camera2.CaptureResult;
import android.hardware.camera2.TotalCaptureResult;
import android.hardware.camera2.params.OutputConfiguration;
import android.hardware.camera2.params.SessionConfiguration;
import android.hardware.camera2.params.StreamConfigurationMap;
import android.media.ImageReader;
import android.opengl.GLSurfaceView;
import android.os.AsyncTask;
import android.os.Handler;
import android.os.HandlerThread;
import android.util.Log;
import android.util.Size;
import android.util.SparseIntArray;
import android.view.Surface;
import android.view.SurfaceHolder;
import android.view.ViewGroup;
import android.widget.Toast;

import androidx.annotation.NonNull;
import androidx.core.app.ActivityCompat;

import com.shm.singlecamera.Utils.SizeUtils;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Set;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;

public class SingleCamera {

    private static final SparseIntArray ORIENTATIONS = new SparseIntArray();
    private static final String TAG = "Camera2";
    
    private float minFocusDistance;
    private float maxFocusDistance;
    
    private Set<String> physicsCameras;

    static {
        ORIENTATIONS.append(Surface.ROTATION_0, 90);
        ORIENTATIONS.append(Surface.ROTATION_90, 0);
        ORIENTATIONS.append(Surface.ROTATION_180, 270);
        ORIENTATIONS.append(Surface.ROTATION_270, 180);
    }

    private final Semaphore cameraOpenCloseLock = new Semaphore(1);
    private final ImageProcess imageListener;
    private Size inputSize;
    private final GLSurfaceView previewView;
    private final ConnectionCallback cameraConnectionCallback;

    // 在类中定义成员变量
    private CaptureRequest request;
    private final CameraCaptureSession.CaptureCallback captureCallback =
            new CameraCaptureSession.CaptureCallback() {
                @Override
                public void onCaptureProgressed(
                        @NonNull final CameraCaptureSession session,
                        @NonNull final CaptureRequest previewRequest,
                        @NonNull final CaptureResult partialResult) {
                }

                @Override
                public void onCaptureCompleted(
                        @NonNull final CameraCaptureSession session,
                        @NonNull final CaptureRequest previewRequest,
                        @NonNull final TotalCaptureResult result) {
                }

            };
    private String logicalCamera;
    private String physicalCamera;
    private CameraCaptureSession captureSession;
    private CameraDevice cameraDevice;
    private Integer sensorOrientation;
    private Size previewSize;
    
    private HandlerThread backgroundHandlerThread;
    private Handler backgroundHandler;
    
    private ImageReader imageReader;
    
    private CaptureRequest.Builder requestBuilder;
    
    private final CameraDevice.StateCallback stateCallback =
            new CameraDevice.StateCallback() {
                @Override
                public void onOpened(@NonNull final CameraDevice cd) {
                    cameraOpenCloseLock.release();
                    cameraDevice = cd;
                    createCameraPreviewSession();

                }

                @Override
                public void onDisconnected(final CameraDevice cd) {
                    cameraOpenCloseLock.release();
                    cd.close();
                }

                @Override
                public void onError(final CameraDevice cd, final int error) {
                    cameraOpenCloseLock.release();
                    cd.close();

                    Log.e(TAG, "cameraDevice onError: " + error);
                }

                @Override
                public void onClosed(@NonNull CameraDevice camera) {
                    super.onClosed(camera);
                    cameraOpenCloseLock.release();

                    Log.e(TAG, "cameraDevice onClosed");

                    if(needClose){
                        backgroundHandlerThread.quitSafely();

                        if (null != imageReader) {
                            // imageListener.shutdown();
                            imageReader.close();
                            imageReader = null;
                        }

                        closeStateCallback.onClosed();
                        needClose = false;
                    }


                }
            };

    private final SurfaceHolder.Callback surfaceTextureListener =
            new  SurfaceHolder.Callback() {

                @Override
                public void surfaceCreated(SurfaceHolder holder) {
                    // Surface 已创建，OpenGL ES 初始化代码，例如创建 EGL 环境
                    // **在这里调用 openCamera() 是安全的，因为 Surface 已经可用**
                    openCamera(holder.getSurfaceFrame().width(), holder.getSurfaceFrame().height());
                }

                @Override
                public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
                    // Surface 尺寸变化处理，例如设置视口和投影矩阵
                }

                @Override
                public void surfaceDestroyed(SurfaceHolder holder) {
                    // Surface 已销毁，OpenGL ES 资源释放代码，例如释放 EGL 环境

                }
            };

    private final Activity activity;
    private CameraStateMachine.CloseStateCallback closeStateCallback;
    private CameraStateMachine.OpenStateCallback openStateCallback;
    private boolean needClose = false;

    public SingleCamera(
            Activity activity,
            GLSurfaceView previewView,
            final ConnectionCallback connectionCallback,
            final ImageProcess imageListener) {
        this.activity = activity;
        this.previewView = previewView;
        this.cameraConnectionCallback = connectionCallback;
        this.imageListener = imageListener;
        
    }

    public void openCamera(Size desiredSize, CameraStateMachine.OpenStateCallback openStateCallback) {


        this.openStateCallback = openStateCallback;

        this.backgroundHandlerThread = new HandlerThread(TAG);
        this.backgroundHandlerThread.start();

        this.backgroundHandler = new Handler(backgroundHandlerThread.getLooper());

        inputSize = desiredSize;

        if (CameraStateMachine.isSurfaceAvailable()) {
            openCamera(inputSize.getWidth(), inputSize.getHeight());
        } else {
            previewView.getHolder().addCallback(surfaceTextureListener);
        }
    }

    private void setUpCameraOutputs(CameraStateMachine.TextureAdjustStateCallback textureAdjustStateCallback) {

        final CameraManager manager = (CameraManager) activity.getSystemService(Context.CAMERA_SERVICE);
        try {
            final CameraCharacteristics characteristics = manager.getCameraCharacteristics(logicalCamera);

            // 获取焦距校准质量
            // focusCalibrationQuality = characteristics.get(CameraCharacteristics.LENS_INFO_FOCUS_DISTANCE_CALIBRATION);
            // 获取最小焦距
            minFocusDistance = characteristics.get(CameraCharacteristics.LENS_INFO_MINIMUM_FOCUS_DISTANCE);
            maxFocusDistance = 0.0f;

            final StreamConfigurationMap map =
                    characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);


            sensorOrientation = characteristics.get(CameraCharacteristics.SENSOR_ORIENTATION);

            // We fit the aspect ratio of TextureView to the size of preview we picked.
            final int orientation = activity.getResources().getConfiguration().orientation;

            assert map != null;

            if(previewSize == null){
                previewSize = SizeUtils.chooseOptimalSize(
                        map.getOutputSizes(SurfaceTexture.class),
                        inputSize.getWidth(),
                        inputSize.getHeight());
            }

            Log.e(TAG, "input size = " + inputSize);
            Log.e(TAG, "preview size = " + previewSize);

            activity.runOnUiThread(new Runnable() {

                public void run() {
                    ViewGroup.LayoutParams params = previewView.getLayoutParams();
                    params.width = previewSize.getWidth();  // 目标宽度
                    params.height = previewSize.getHeight(); // 目标高度
                    previewView.setLayoutParams(params);

                  textureAdjustStateCallback.onAdjusted();
                }
            });

        } catch (final CameraAccessException e) {
            Log.e(String.valueOf(e),"Exception!");
        }

        cameraConnectionCallback.onPreviewSizeChosen(previewSize, sensorOrientation);
    }

    public void setLogicalCamera(String logicalCamera) {
        this.logicalCamera = logicalCamera;
    }

    public void setPhysicalCamera(String physicalCamera) {
        this.physicalCamera = physicalCamera;
    }

    private void openCamera(final int width, final int height) {

        if(logicalCamera == null){
            Toast.makeText(activity, "this device does not select camera", Toast.LENGTH_SHORT).show();
            return;
        }

        // 在回调里打开相机，保证texture的尺寸调整完成
        setUpCameraOutputs(new CameraStateMachine.TextureAdjustStateCallback() {
            @Override
            public void onAdjusted() {

                final CameraManager manager = (CameraManager) activity.getSystemService(Context.CAMERA_SERVICE);
                try {
                    if (!cameraOpenCloseLock.tryAcquire(5000, TimeUnit.MILLISECONDS)) {
                        throw new RuntimeException("Time out waiting to lock camera opening.");
                    }
                    if (ActivityCompat.checkSelfPermission(activity, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
                        // 申请权限
                        String[] permissions = {Manifest.permission.CAMERA};
                        activity.requestPermissions(permissions, 100);
                    }else{
                        imageListener.start();
                        manager.openCamera(logicalCamera, stateCallback, backgroundHandler);
                    }
                } catch (final CameraAccessException e) {
                    Log.e(String.valueOf(e), "Exception!");
                } catch (final InterruptedException e) {
                    throw new RuntimeException("Interrupted while trying to lock camera opening.", e);
                }
            }
        });
    }


    public void closeCamera(CameraStateMachine.CloseStateCallback callback) {
        try {

            this.needClose = true;

            this.closeStateCallback = callback;

            cameraOpenCloseLock.acquire();

            if (captureSession != null) {
                captureSession.stopRepeating(); // 停止重复请求
            }


        }  catch (CameraAccessException | InterruptedException e) {
            throw new RuntimeException(e);
        }

    }

    private void createCameraPreviewSession() {

        // Create the reader for the preview frames.
        imageReader = ImageReader.newInstance(previewSize.getWidth(), previewSize.getHeight(), ImageFormat.YUV_420_888, 5);
        imageReader.setOnImageAvailableListener(imageListener, imageListener.getBackgroundHandler());
        Surface process = imageReader.getSurface();

        imageListener.setPreviewSize(previewSize);

        try {
            //构建输出参数  在参数中设置物理摄像头
            List<OutputConfiguration> configurations = new ArrayList<>();

            requestBuilder = cameraDevice.createCaptureRequest(CameraStateMachine.CAMERA_TEMPLATE);


            //构建输出参数  在参数中设置物理摄像头
            OutputConfiguration outputConfiguration = new OutputConfiguration(process);
            outputConfiguration.setPhysicalCameraId(physicalCamera);

            configurations.add(outputConfiguration);
            requestBuilder.addTarget(process);

            // 开启任务
            cameraDevice.createCaptureSession(Arrays.asList(process),
                    new CameraCaptureSession.StateCallback() {

                        @Override
                        public void onReady(@NonNull CameraCaptureSession session) {
                            super.onReady(session);

                            if(needClose){
                                captureSession.close();
                                captureSession = null;
                            }

                        }

                        @Override
                        public void onClosed(@NonNull CameraCaptureSession session) {
                            super.onClosed(session);

                            if(needClose){
                                if (null != cameraDevice) {
                                    cameraDevice.close();
                                    cameraDevice = null;
                                }
                            }


                            Log.e(TAG, "session onClosed");
                        }

                        @Override
                        public void onConfigured(@NonNull CameraCaptureSession cameraCaptureSession) {
                            try {
                                captureSession = cameraCaptureSession;

                                // 配置第一个物理摄像头
                                configurePhysicalCamera(requestBuilder);

                                request = requestBuilder.build();

                                captureSession.setRepeatingRequest(
                                        request,
                                        captureCallback,
                                        backgroundHandler);

                                openStateCallback.onOpened();
                            } catch (CameraAccessException e) {
                                e.printStackTrace();
                            }
                        }
                        @Override
                        public void onConfigureFailed(@NonNull CameraCaptureSession cameraCaptureSession) {

                        }
                    },null);


        } catch (CameraAccessException e) {
            e.printStackTrace();
        }

    }

    private void configurePhysicalCamera(CaptureRequest.Builder builder) throws CameraAccessException {
        // 为特定的物理摄像头设置参数
//        builder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_AUTO);
        builder.set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_ON);
        builder.set(CaptureRequest.CONTROL_AWB_MODE, CaptureRequest.CONTROL_AWB_MODE_AUTO);
    }

    public void pauseCapture() throws CameraAccessException {
        captureSession.stopRepeating();
    }

    public void continueCapture() throws CameraAccessException {
        captureSession.setRepeatingRequest(
                request,
                captureCallback,
                backgroundHandler);
    }


    public interface ConnectionCallback {
        void onPreviewSizeChosen(Size size, int cameraRotation);
    }
    

    public void setFocusDistance(float ratio) {

        if(captureSession == null) return;

        try{
            captureSession.stopRepeating();
            
            // 设置手动对焦模式
            requestBuilder.set(
                    CaptureRequest.CONTROL_AF_MODE,
                    CaptureRequest.CONTROL_AF_MODE_OFF);

            // 设置焦点距离，focusDistance 应该是一个非负数，表示距离镜头的焦距 1/meter。
            float focusDistance = Math.max(minFocusDistance - ratio * (minFocusDistance - maxFocusDistance), 0.f);

            // 设置手动对焦模式
            requestBuilder.set(
                    CaptureRequest.LENS_FOCUS_DISTANCE,
                    focusDistance);


            // 构建请求并发送给相机设备
            request = requestBuilder.build();

            captureSession.setRepeatingRequest(
                    request,
                    captureCallback,
                    backgroundHandler);

        } catch (CameraAccessException e) {
            throw new RuntimeException(e);
        }
        
    }

    public void lockAE(){

        if(captureSession == null) return;

        try {
            captureSession.stopRepeating();

            // 设置手动对焦模式。
            requestBuilder.set(
                    CaptureRequest.CONTROL_AE_LOCK,
                    true);

            // 构建请求并发送给相机设备。
            request = requestBuilder.build();

            captureSession.setRepeatingRequest(
                    request,
                    captureCallback,
                    backgroundHandler);
            
        } catch (CameraAccessException e) {
            throw new RuntimeException(e);
        }
        
    }

    public void unlockAE(){

        if(captureSession == null) return;

        try {
            captureSession.stopRepeating();

            // 设置手动对焦模式。
           requestBuilder.set(
                    CaptureRequest.CONTROL_AE_LOCK,
                    false);

            // 构建请求并发送给相机设备。
            request = requestBuilder.build();

            captureSession.setRepeatingRequest(
                    request,
                    captureCallback,
                    backgroundHandler);

        } catch (CameraAccessException e) {
            throw new RuntimeException(e);
        }

    }


    public void lockAWB(){

        if(captureSession == null) return;

        try {
            captureSession.stopRepeating();

            // 设置手动对焦模式。
            requestBuilder.set(
                    CaptureRequest.CONTROL_AWB_LOCK,
                    true);

            // 构建请求并发送给相机设备。
            request = requestBuilder.build();

            captureSession.setRepeatingRequest(
                    request,
                    captureCallback,
                    backgroundHandler);

        } catch (CameraAccessException e) {
            throw new RuntimeException(e);
        }

    }

    public void unlockAWB(){

        if(captureSession == null) return;

        try {
            captureSession.stopRepeating();

            // 设置手动对焦模式。
            requestBuilder.set(
                    CaptureRequest.CONTROL_AWB_LOCK,
                    false);

            // 构建请求并发送给相机设备。
            request = requestBuilder.build();

            captureSession.setRepeatingRequest(
                    request,
                    captureCallback,
                    backgroundHandler);

        } catch (CameraAccessException e) {
            throw new RuntimeException(e);
        }

    }
}
